import { ElMessage } from 'element-plus';
import { Socket } from 'socket.io-client';
import { aesDecrypt, keyNegotiation } from './crypto';

/**
 * 消息接收器
 * ***
 * 用于接收websocket消息和ipc消息，并通过ipc与客户端对象进行交互
 */
export class Receiver {
  protected socket: Socket;
  protected rsaPublicKey: string = '';
  protected dhPublicKey: string = '';
  protected sign: string = '';
  protected userId: string = '';
  protected name: string = '';
  protected targetInfoMap: Map<
    string,
    {
      userId: string;
      dhPublicKey: string;
      rsaPublicKey: string;
      sign: string;
    }
  > = new Map();
  protected targetInfoSet: Set<string> = new Set();
  constructor(socket: Socket) {
    this.socket = socket;
  }
  init() {
    window.electron.ipcRenderer.on('key-init-status', (_event, status) => {
      if (status) {
        window.electron.ipcRenderer.send('get-public-key');
        window.electron.ipcRenderer.once(
          'get-public-key',
          (
            _event,
            data: {
              rsaKeyPair: {
                publicKey: string;
              };
              dhKeyPair: {
                publicKey: string;
              };
              sign: string;
            }
          ) => {
            if (
              !data.hasOwnProperty('rsaKeyPair') ||
              !data.hasOwnProperty('dhKeyPair') ||
              !data.hasOwnProperty('sign')
            ) {
              console.log('客户端接收器：未能从主进程中获取足够的公钥信息');
              return;
            }

            this.rsaPublicKey = data.rsaKeyPair.publicKey;
            this.dhPublicKey = data.dhKeyPair.publicKey;
            this.sign = data.sign;
            console.log('客户端接收器：初始化成功，签名：', this.sign);
          }
        );
      }
    });

    this.socket.on(
      'client-connection',
      (data: { userId: string; msg: string }) => {
        console.log(data.msg);
      }
    );

    this.socket.on(
      'apply-add-friend',
      (data: {
        status: boolean;
        msg: string;
        data: {
          name: string;
          userId: string;
        };
      }) => {
        if (data.status) {
          window.electron.ipcRenderer.send(
            'show-system-message',
            '有人申请添加好友',
            `申请人:${data.data.name},ID:${data.data.userId}`
          );
          console.log('新的好友申请：', data.data);
        } else {
          ElMessage({
            message: data.msg,
            type: 'warning',
            duration: 2000
          });
        }
      }
    );

    this.socket.on(
      'apply-add-friend-ack',
      (data: { status: boolean; msg: string }) => {
        if (data.status) {
          ElMessage({
            message: data.msg,
            type: 'success',
            duration: 2000
          });
        } else {
          ElMessage({
            message: data.msg,
            type: 'warning',
            duration: 2000
          });
        }
      }
    );

    //同意好友申请
    this.socket.on('agree-add-friend', (data) => {
      window.electron.ipcRenderer.send(
        'show-system-message',
        '好友通知',
        `对方已同意`
      );
      window.electron.ipcRenderer.send('ipc:refresh-friend-list');
      console.log('对方已同意添加好友，同意信息：', data);
    });

    //拒绝好友申请
    this.socket.on('reject-add-friend', () => {
      window.electron.ipcRenderer.send(
        'show-system-message',
        '好友通知',
        `对方拒绝了你的申请`
      );
    });

    this.socket.on('sender-key', (res) => {
      console.log('发送者密钥信息：', res);
    });

    /**
     * 接收密钥协商请求
     */
    this.socket.on(
      'apply-private-chat',
      async (data: {
        receiverId: string;
        senderInfo: {
          senderId: string;
          dhPublicKey: string;
          rsaPublicKey: string;
          sign: string;
        };
      }) => {
        //向服务器发送公钥相关信息
        this.socket.emit('get-public-key', {
          userId: this.userId,
          dhPublicKey: this.dhPublicKey,
          rsaPublicKey: this.rsaPublicKey,
          sign: this.sign
        });

        console.log('有人跟我申请私聊：', data);
        const msg = ElMessage({
          type: 'warning',
          message: '有人跟我申请私聊，正在处理...',
          duration: 0,
          showClose: true
        });

        const status = await keyNegotiation(
          {
            dhPublicKey: data.senderInfo.dhPublicKey,
            rsaPublicKey: data.senderInfo.rsaPublicKey,
            sign: data.senderInfo.sign
          },
          data.senderInfo.senderId
        );

        msg.close();
        if (!status) {
          ElMessage({
            type: 'warning',
            message: '密钥协商失败',
            duration: 0,
            showClose: true
          });
        }

        //由私聊被接收者向私聊申请者发送密钥协商结果
        this.socket.emit('key-agreement-result', {
          senderId: this.userId,
          receiverId: data.senderInfo.senderId,
          status: status
        });

        window.electron.ipcRenderer.send(
          'ipc:add-key-negotiation-user',
          data.senderInfo.senderId
        );

        ElMessage({
          type: 'success',
          message: '密钥协商成功',
          duration: 2000,
          showClose: true
        });

        console.log('被申请者-ipc密钥协商状态：', status);
      }
    );

    //监听目标发送的密钥协商信息
    this.socket.on(
      'target-negotiation-info',
      async (data: {
        userId: string;
        dhPublicKey: string;
        rsaPublicKey: string;
        sign: string;
      }) => {
        const msg = ElMessage({
          type: 'warning',
          message: '正在密钥协商...',
          duration: 0,
          showClose: true
        });

        console.log('目标用户发来的公钥信息：', data);
        //将目标用户的公钥信息保存, 并且判断是否已存在该用户密钥信息
        if (this.targetInfoSet.has(data.userId) === false) {
          this.targetInfoSet.add(data.userId);
        } else {
          this.targetInfoMap.delete(data.userId);
          this.targetInfoSet.delete(data.userId);
        }

        //无论何时都获取目标用户最新的公钥信息并保存
        this.targetInfoMap.set(data.userId, {
          userId: data.userId,
          dhPublicKey: data.dhPublicKey,
          rsaPublicKey: data.rsaPublicKey,
          sign: data.sign
        });

        const status = await keyNegotiation(
          {
            dhPublicKey: data.dhPublicKey,
            rsaPublicKey: data.rsaPublicKey,
            sign: data.sign
          },
          data.userId
        );

        msg.close();
        this.socket.emit('key-agreement-result', {
          senderId: this.userId,
          receiverId: data.userId,
          status: status
        });
        window.electron.ipcRenderer.send('ipc:key-negotiation-status', status);

        if (status) {
          ElMessage({
            type: 'success',
            message: '密钥协商成功',
            duration: 2000,
            showClose: true
          });
        } else {
          ElMessage({
            type: 'error',
            message: '密钥协商失败',
            duration: 0,
            showClose: true
          });
        }
      }
    );

    //监听服务器转发的密钥协商结果
    this.socket.on(
      'key-agreement-result',
      (data: { senderId: string; receiverId: string; status: boolean }) => {
        console.log('服务器响应-密钥协商结果：', data);
      }
    );

    this.socket.on('server-response', (res) => {
      if (res.method === 'user-online') {
        console.log('服务端响应-用户在线状态：', res.data);
        window.electron.ipcRenderer.send('ipc:user-no-online', res.data);
      }
    });

    /**
     * 监听对方发送的私聊消息
     * 解密明文和保存消息记录
     */
    this.socket.on(
      'private-chat-message',
      async (res: {
        senderId: string;
        receiverId: string;
        time: number;
        msg: {
          iv: string;
          encrypted: string;
        };
        senderName: string;
      }) => {
        console.log('私聊消息：', res);
        try {
          //解密消息成明文
          const plainText = await aesDecrypt(
            res.senderId,
            res.msg.iv,
            res.msg.encrypted
          );

          //保存消息记录
          this.saveMessage(
            res.senderId,
            res.receiverId,
            res.time,
            plainText,
            res.senderName
          ).then((res) => {
            console.log('保存私聊消息成功：', res);
          });

          //解密成功，把socket消息推送到聊天窗口
          window.electron.ipcRenderer.send(
            'ipc:put-socket-message',
            res.senderId,
            res.senderName,
            res.time,
            plainText
          );

          //获取窗口状态，如果窗口最小化则显示系统消息
          const windowMinSizeStatus = this.getWindowMinSize();
          windowMinSizeStatus.then((status) => {
            if (status === true) {
              window.electron.ipcRenderer.send(
                'show-system-message',
                `${res.senderName}的消息`,
                plainText
              );
            }
          });
          console.log('明文消息：', plainText);
        } catch (error: any) {
          console.log('接收器-处理消息出错：', error);
        }
      }
    );

    /**
     * 监听消息发送结果
     */
    this.socket.on(
      'private-chat-message-ack',
      (data: {
        status: boolean;
        msg: string;
        senderId: string;
        receiverId: string;
        time: number;
      }) => {
        if (!data.status) {
          ElMessage({
            type: 'error',
            message: data.msg,
            duration: 1000,
            showClose: true
          });
        } else {
          window.electron.ipcRenderer.send('save-own-message', data.time);
          window.electron.ipcRenderer.once('save-own-message', (_, status) => {
            console.log('消息保存状态：', status);
          });
        }
      }
    );

    this.socket.on(
      'delete-friend',
      (res: {
        code: 0 | 1 | 2; //0表示成功，1表示删除失败，2表示被删除方接收到的消息
        msg: string;
        friendId: string;
        senderId: string;
      }) => {
        if (res.code === 2) {
          // TODO 如果通信窗口是与当前人通信的话，那则把按钮状态改成不可用
          window.electron.ipcRenderer.send(
            'ipc:change-submit-message-button',
            res.senderId,
            res.friendId,
            false
          );

          // 跟主进程推送系统通知
          window.electron.ipcRenderer.send(
            'show-system-message',
            '系统消息',
            res.msg
          );
        } else {
          ElMessage({
            type: res.code === 0 ? 'success' : 'error',
            message: res.msg,
            duration: 2000,
            showClose: true
          });
          if (res.code === 0) {
            window.electron.ipcRenderer.send('ipc:close-chat-window');
          }
        }

        window.electron.ipcRenderer.send('ipc:refresh-friend-list');
      }
    );

    this.socket.on(
      'client-online-status',
      (clientsOnline: Array<{ id: string; isLine: boolean }>) => {
        window.electron.ipcRenderer.send(
          'ipc:client-online-status',
          clientsOnline
        );
      }
    );

    this.socket.on('friend-online', (friendId: string, status: boolean) => {
      window.electron.ipcRenderer.send('ipc:friend-online', friendId, status);
      if (status === false) {
        window.electron.ipcRenderer.send(
          'ipc:remove-key-negotiation-user',
          friendId
        );
      }
    });
  }

  /**
   * 保存消息记录
   * @param senderId 发送方id
   * @param receiverId 接收方id
   * @param time 时间戳
   * @param plainText 明文消息
   * @returns {Promise<boolean>}
   */
  saveMessage(
    senderId: string,
    receiverId: string,
    time: number,
    plainText: string,
    senderName: string
  ): Promise<boolean> {
    return new Promise((resolve, _reject) => {
      //把明文保存到json文件中
      window.electron.ipcRenderer.send(
        'save-target-message',
        senderId,
        receiverId,
        time,
        plainText,
        senderName
      );
      window.electron.ipcRenderer.on('save-target-message', (_, res) => {
        resolve(res);
      });
    });
  }

  getWindowMinSize(): Promise<boolean> {
    return new Promise((resolve, _) => {
      window.electron.ipcRenderer.send('window-minimized');
      window.electron.ipcRenderer.once(
        'window-minimized',
        (_, status: boolean) => {
          resolve(status);
        }
      );
    });
  }
}
